package com.noties.markwon.utils;

import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import ohos.agp.utils.Matrix;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.Resource;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;

/**
 * @author: yangrui
 * @function: replace BitmapFactory
 * @date: 2021/3/23
 */
public class PixelMapUtils {

    private static final String HI_LOG_LABEL = "PixelMapUtils";

    /**
     * Create a PixelMap with specific width and height
     * @param width The PixelMap width
     * @param height The PixelMap height
     * @param pixelFormat Indicates the expected format of the pixel map to be created.
     * @return A editable PixelMap
     */
    public static PixelMap createBitmap(int width, int height, PixelFormat pixelFormat) {
        PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
        initializationOptions.size = new Size(width, height);
        initializationOptions.pixelFormat = pixelFormat;
        initializationOptions.editable = true;
        return PixelMap.create(initializationOptions);
    }

    /**
     * Returns an immutable bitmap from subset of the source bitmap,
     * transformed by the optional matrix. The new bitmap may be the
     * same object as source, or a copy may have been made. It is
     * initialized with the same density and color space as the original
     * bitmap.
     *
     * If the source bitmap is immutable and the requested subset is the
     * same as the source bitmap itself, then the source bitmap is
     * returned and no new bitmap is created.
     *
     * @param source   The bitmap we are subsetting
     * @param x        The x coordinate of the first pixel in source
     * @param y        The y coordinate of the first pixel in source
     * @param width    The number of pixels in each row
     * @param height   The number of rows
     * @param m        Optional matrix to be applied to the pixels, current not working
     * @param filter   true if the source should be filtered, current not working
     * @return A bitmap that represents the specified subset of source
     */
    public static PixelMap createBitmap(PixelMap source, int x, int y, int width, int height,
                                        Matrix m, boolean filter) {
        PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
        initializationOptions.size = new Size(width, height);
        initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
        return PixelMap.create(source, new Rect(x, y, width, height), initializationOptions);
    }

    /**
     * Parse a file to a PixelMap
     * @param inputFile The file path string
     * @return The PixelMap
     */
    public static PixelMap decodeFile(String inputFile) {
        if (TextUtils.isEmpty(inputFile)) {
            return null;
        }
        File file = new File(inputFile);
        ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
        sourceOptions.formatHint = "image/jpeg";
        ImageSource imageSource = ImageSource.create(file, sourceOptions);
        PixelMap pixelMap = getPixelMapFromImageSource(imageSource);
        imageSource.release();
        return pixelMap;
    }

    /**
     * Parse a file to a PixelMap
     * @param fileDescriptor the file
     * @return a PixelMap
     */
    public static PixelMap decodeFile(FileDescriptor fileDescriptor) {
        ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
        sourceOptions.formatHint = "image/jpeg";
        ImageSource imageSource = ImageSource.create(fileDescriptor, sourceOptions);
        PixelMap pixelMap = getPixelMapFromImageSource(imageSource);
        imageSource.release();
        return pixelMap;
    }

    /**
     * Parse a inputStream to a PixelMap
     * @param inputStream the input stream
     * @return a PixelMap
     */
    public static PixelMap decodeStream(InputStream inputStream) {
        ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
        sourceOptions.formatHint = "image/jpeg";
        ImageSource imageSource = ImageSource.create(inputStream, sourceOptions);
        PixelMap pixelMap = getPixelMapFromImageSource(imageSource);
        imageSource.release();
        return pixelMap;
    }

    /**
     * Parse a inputStream to a PixelMap
     * @param inputStream the input stream
     * @return a PixelMap
     */
    public static PixelMap decodeStream(InputStream inputStream,Object object,ImageSource.DecodingOptions options) {
        ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
        sourceOptions.formatHint = "image/jpeg";
        ImageSource imageSource = ImageSource.create(inputStream, sourceOptions);
        PixelMap pixelMap = imageSource.createPixelmap(options);
        imageSource.release();
        return pixelMap;
    }
    private static PixelMap getPixelMapFromImageSource(ImageSource imageSource) {
        if (imageSource == null) {
            LogUtils.d(HI_LOG_LABEL, "imageSource is empty");
            return null;
        }
        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
        decodingOptions.desiredSize = new Size(0, 0);
        decodingOptions.desiredRegion = new Rect(0, 0, 0, 0);
        decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;
        return imageSource.createPixelmap(decodingOptions);
    }



    /**
     * Compress the pixel map to the target output stream
     * @param pixelMap The original pixel map.
     * @param compressFormat Indicates the output image format represented by an MIME type string. image/jpeg is supported.
     * @param quality Indicates the image compression quality. The value ranges from 0 to 100. A larger value indicates better image quality but larger space occupied.
     * @param outputStream The target output stream.
     */
    public static void compress(PixelMap pixelMap, String compressFormat, int quality, OutputStream outputStream) {
        if (pixelMap == null) {
            LogUtils.e(HI_LOG_LABEL, "pixelMap is empty");
            return;
        }
        ImagePacker imagePacker = ImagePacker.create();
        ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
        packingOptions.format = compressFormat;
        packingOptions.quality = quality;
        boolean result = imagePacker.initializePacking(outputStream, packingOptions);
        if (result) {
            imagePacker.addImage(pixelMap);
            imagePacker.finalizePacking();
        } else {
            LogUtils.e(HI_LOG_LABEL, "imagePacker failed to initial");
        }
    }

    /**
     * Get the pixel map from resource id
     * @param context The context
     * @param drawableId the resource id in resource table
     */
    public static PixelMap getPixelMapFromResource(Context context, int drawableId) {
        Resource resource;
        try {
            resource = context.getResourceManager().getResource(drawableId);
            return decodeStream(resource);
        } catch (IOException | NotExistException e) {
            e.printStackTrace();
        }
        return null;
    }
}
